package com.totem.base.filter.support;

import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.Method;
import com.totem.base.constants.CommonConstants;
import com.totem.base.constants.ErrorCode;
import com.totem.base.exception.BaseException;
import com.totem.base.util.FilterUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
public class ModifyRequestWrapper extends HttpServletRequestWrapper {
    private AES aes;
    private byte[] body;
    private Map<String, String[]> paramsMap = new LinkedHashMap<>();
    private String queryString = "";

    public ModifyRequestWrapper(HttpServletRequest request, AES aes) {
        super(request);
        this.aes = aes;
        String method = request.getMethod();
        if(StringUtils.equalsIgnoreCase(method, Method.POST.name())) {
            resetBody(request);
        }
        if(StringUtils.equalsIgnoreCase(method, Method.GET.name())) {
            resetParams(request);
        }
    }

    public byte[] getContentAsByteArray() {
        return this.body;
    }

    private void resetBody(HttpServletRequest request)  {
        String requestString = FilterUtil.getRequestString(request);
        if(StringUtils.isBlank(requestString)){
            throw new BaseException(ErrorCode.BAD_REQUEST, "Request body is blank.");
        }
        log.debug("requestString:{}", requestString);
        byte[] requestBytes = Base64.getDecoder().decode(requestString);
        String requestDecrypted = aes.decryptStr(requestBytes);
        requestDecrypted = requestDecrypted.replaceAll("\u00a0", " ");
        requestDecrypted = requestDecrypted.replaceAll("\\s+", " ");
        log.debug("requestDecrypted:{}", requestDecrypted);
        body = requestDecrypted.getBytes(CharsetUtil.CHARSET_UTF_8);
    }

    private void resetParams(HttpServletRequest request){
        String requestString = FilterUtil.getRequestString(request);
        if(StringUtils.isBlank(requestString)){
            throw new BaseException(ErrorCode.BAD_REQUEST, "Url params is blank.");
        }
        String[] urlParams = requestString.split("=");
        if(urlParams.length!=2 || StringUtils.isBlank(urlParams[1])){
            throw new BaseException(ErrorCode.BAD_REQUEST, String.format("Params template: %s", CommonConstants.PARAMS_TEMPLATE));
        }
        requestString = aes.decryptStr(urlParams[1]);
        log.debug("requestString:{}", requestString);
        this.queryString = requestString;
        Map queryMap = UrlQuery.of(queryString, StandardCharsets.UTF_8).getQueryMap();
        Iterator iterator = queryMap.keySet().iterator();
        while (iterator.hasNext()){
            Object key = iterator.next();
            Object val = queryMap.get(key);
            String[] newVal = new String[]{String.valueOf(val)};
            paramsMap.put(String.valueOf(key), newVal);
        }
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream(){
            @Override
            public int read() throws IOException {
                return bais.read();
            }
            @Override
            public boolean isFinished() {
                return bais.available()==0;
            }
            @Override
            public boolean isReady() {
                return true;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

    public void addParameter(String name,String value) {
        String[] parameterValues = getParameterValues(name);
        if (parameterValues == null) {
            this.paramsMap.put(name, new String[] {value});
        }
    }

    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<String>(paramsMap.keySet());
        return vector.elements();
    }

    @Override
    public String getParameter(String name) {
        if(paramsMap.containsKey(name)) {
            return paramsMap.get(name).length > 0 ? paramsMap.get(name)[0] : "";
        }
        return "";
    }
    @Override
    public String[] getParameterValues(String name) {
        return paramsMap.get(name);
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        return Collections.unmodifiableMap(paramsMap);
    }

    @Override
    public String getQueryString(){
        return StringUtils.isNotBlank(this.queryString)?this.queryString:"";
    }
}
